package sketch;

import java.lang.reflect.Constructor;
import java.lang.reflect.Method;
import java.util.Collection;
import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;

import randoop.RConstructor;
import randoop.RMethod;
import randoop.StatementKind;
import randoop.util.Reflection;
import sketch.util.SketchUtil;

/**
 * Keep track of all loaded classes
 * */
public class ClassCollections {
	
	public Collection<Class<?>> classes = new LinkedHashSet<Class<?>>();
	
	public Collection<StatementKind> statements = new LinkedHashSet<StatementKind>();
	
	public Map<Class<?>, Map<String, StatementKind>> methodMappings =
		new LinkedHashMap<Class<?>, Map<String, StatementKind>>();
	
	public void addToClassesCollection(Collection<Class<?>> classes) {
		for(Class<?> clazz : classes) {
			this.classes.add(clazz);
		}
		//add to statement collection
		this.addToStatementCollection(classes);
	}
	
	private void addToStatementCollection(Collection<Class<?>> classes) {
		List<StatementKind> stmts = Reflection.getStatements(classes, null);
		statements.addAll(stmts);
		//update the map
		for(StatementKind stmt : stmts) {
			if(stmt instanceof RMethod) {
				RMethod rmethod = (RMethod)stmt;
				Method method = rmethod.getMethod();
				String signature = SketchUtil.getMethodNameForSketch(method);
				Class<?> declaringClass = method.getDeclaringClass();
				//System.out.println(declaringClass  + " : " + signature);
				//now it is only the declaring method
				this.updateMethodMappings(declaringClass, signature, stmt);
			} else if (stmt instanceof RConstructor) {
				RConstructor rconstructor = (RConstructor)stmt;
				Constructor<?> constructor = rconstructor.getConstructor();
				String signature = SketchUtil.getConstructorNameForSketch(constructor);
				Class<?> declaringClass = constructor.getDeclaringClass();
				//System.out.println(declaringClass  + " : " + signature);
				this.updateMethodMappings(declaringClass, signature, stmt);
			} else {
				throw new RuntimeException("Should not have the statement type: " + stmt);
			}
		}
	}
	
	//update the map
	private void updateMethodMappings(Class<?> clazz, String methodName, StatementKind statement) {
		if(!methodMappings.containsKey(clazz)) {
			Map<String, StatementKind> methodNameMapping = new LinkedHashMap<String, StatementKind>();
			methodMappings.put(clazz, methodNameMapping);
		}
		Map<String, StatementKind> methodNameMapping = methodMappings.get(clazz);
		methodNameMapping.put(methodName, statement);
	}
	
	public StatementKind lookforStatement(Class<?> clazz, String methodSignature) {
		if(!methodMappings.containsKey(clazz)) {
			return null;
		}
		Map<String, StatementKind> methodNames = methodMappings.get(clazz);
		//it could return null
		return methodNames.get(methodSignature);
	}
	
	public static void main(String[] args) {
		ClassCollections collections = new ClassCollections();
		Collection<Class<?>> classes = new LinkedList<Class<?>>();
		classes.add(String.class);
		collections.addToClassesCollection(classes);
		
		System.out.println(collections.lookforStatement(String.class, "indexOf(int)"));
	}
}